from classes.TestStep import TestStep
from classes.TestCase import TestCase
from classes.PageElementFactory import PageElementFactory
import requests
import json
import time
import os
import os
from wcwidth import wcswidth  # 需提前 pip install wcwidth
def get_input_value(input_data, key, default=0):
    """优先从 input_data 取 key，否则用默认值"""
    if input_data and key in input_data:
        return input_data[key]
    return default

ELEMENT_JSON_PATH = "resources/elements.json"
# 2. SCMAutoTestManager类，管理用例库
class SCMAutoTestManager:
    def __init__(self, api_url, element_json_path=ELEMENT_JSON_PATH):
        self.api_url = api_url
        self.elements = self.load_elements(element_json_path)
        self.case_library = {}
        self._init_cases()

    def load_elements(self, path):
        with open(path, "r", encoding="utf-8") as f:
            element_dicts = json.load(f)
        return {e["name"]: PageElementFactory.from_dicts([e])[0] for e in element_dicts}

    def _init_cases(self):
        # 可扩展更多用例
        # self.case_library["other_case"] = ...
        pass
    def test_login(self, input_data=None, test_id=None):
        login_case = TestCase("登录测试", browser="chrome", headless=False, keep_browser_open=True)
        login_case.add_step(TestStep("1.打开登录页", "open_url", None, url="http://localhost:80/index", timeout=5))
        login_case.add_step(TestStep("2.等待用户名输入框", "wait_for_element", self.elements["username"], timeout=5))
        username_text = get_input_value(input_data, "username", " ")
        login_case.add_step(TestStep("3.输入用户名", "input_text", self.elements["username"], text=username_text, timeout=5))
        password_text = get_input_value(input_data, "password", " ")
        # login_case.add_step(TestStep("4.输入密码", "input_text", self.elements["password"], text=password_text, timeout=5))
        login_case.add_step(TestStep("5.点击登录按钮", "click", self.elements["login_btn"], timeout=2, retries=3))
        return self.execute_case(login_case, test_id=test_id)


    def test_navigate_customer_basic_info(self, input_data=None, test_id=None):
        navigate_case = TestCase("导航客户基本信息", browser="chrome", headless=False, keep_browser_open=True)
        navigate_case.add_step(TestStep("1.点击基础信息菜单", "click", self.elements["base_info_menu"], timeout=2, retries=3))
        # navigate_case.add_step(TestStep("等待客户信息菜单可点击", "wait_for_element", self.elements["base_info_menu_customer_info"], timeout=5))
        navigate_case.add_step(TestStep("2.点击客户信息菜单", "click", self.elements["base_info_menu_customer_info"], timeout=2, retries=3))
        return self.execute_case(navigate_case, test_id=test_id)


    def test_add_customer_basic_info(self, input_data=None, test_id=None):
        add_customer_case = TestCase("添加客户基本信息", browser="chrome", headless=False, keep_browser_open=True)
        add_customer_case.add_step(TestStep("a.点击新增按钮", "click", self.elements["base_info_menu_customer_info_add"], timeout=1, retries=3))
        add_customer_case.add_step(TestStep("b.等待客户编码输入框", "wait_for_element", self.elements["customer_code"], timeout=10))
        customer_code_text = get_input_value(input_data, "customer_code", "")
        add_customer_case.add_step(TestStep("1.输入客户编码", "input_text", self.elements["customer_code"], text=customer_code_text, timeout=5))
        customer_short_name_text = get_input_value(input_data, "customer_short_name", " ")
        add_customer_case.add_step(TestStep("2.输入客户简称", "input_text", self.elements["customer_short_name"], text=customer_short_name_text, timeout=5))
        customer_full_name_text = get_input_value(input_data, "customer_full_name", " ")
        add_customer_case.add_step(TestStep("3.输入客户全称", "input_text", self.elements["customer_full_name"], text=customer_full_name_text, timeout=5))
        owner_text = get_input_value(input_data, "owner", " ")
        add_customer_case.add_step(TestStep("4.输入负责人", "input_text", self.elements["owner"], text=owner_text, timeout=5))
        phone_text = get_input_value(input_data, "phone", " ")
        add_customer_case.add_step(TestStep("5.输入联系电话", "input_text", self.elements["phone"], text=phone_text, timeout=5))
        address_text = get_input_value(input_data, "address", " ")
        add_customer_case.add_step(TestStep("6.输入联系地址", "input_text", self.elements["address"], text=address_text, timeout=5))
        email_text = get_input_value(input_data, "email", " ")
        add_customer_case.add_step(TestStep("7.输入电子邮箱", "input_text", self.elements["email"], text=email_text, timeout=5))

        # country
        country = get_input_value(input_data, "country", "国内")
        add_customer_case.add_step(TestStep("8.选择国内/国外", "select_elementui_option", self.elements["domesticOrCrossboard"],   option_text=country,timeout=5))

        # add_customer_case.add_step(TestStep("8.选择国家", "click", self.elements["customer_country_select"], timeout=2, retries=3))
        # add_customer_case.add_step(TestStep("9.选择国家选项", "click", self.elements.get(f"customer_country_option_{country}", self.elements["customer_country_option_中国"]), timeout=2, retries=3))

        # business_owner
        business_owner_text = get_input_value(input_data, "business_owner", " ")
        add_customer_case.add_step(TestStep("10.输入客户经理", "input_text", self.elements["business_owner"], text=business_owner_text, timeout=5))

        # credit_code
        credit_code_text = get_input_value(input_data, "credit_code", " ")
        add_customer_case.add_step(TestStep("11.输入客户信用代码", "input_text", self.elements["credit_code"], text=credit_code_text, timeout=5))
        # bank
        bank_text = get_input_value(input_data, "bank", " ")
        add_customer_case.add_step(TestStep("12.输入开户银行", "input_text", self.elements["bank"], text=bank_text, timeout=5))     
        # bank_account
        bank_account_text = get_input_value(input_data, "bank_account", " ")
        add_customer_case.add_step(TestStep("13.输入银行账户", "input_text", self.elements["bank_account"], text=bank_account_text, timeout=5))
        
        # customer_remark
        customer_remark_text = get_input_value(input_data, "customer_remark", " ")
        add_customer_case.add_step(TestStep("14.输入备注", "input_text", self.elements["customer_remark"], text=customer_remark_text, timeout=5))

        # 提交表单
        add_customer_case.add_step(TestStep("15.提交表单", "click", self.elements["customer_basic_info_submit_btn"], timeout=5))
        return self.execute_case(add_customer_case, test_id=test_id)


    def test_navigate_supplier_basic_info(self, input_data=None, test_id=None):
            navigate_case = TestCase("导航供应商基本信息", browser="chrome", headless=False, keep_browser_open=True)
            navigate_case.add_step(TestStep("1.点击基础信息菜单", "click", self.elements["base_info_menu"], timeout=2, retries=3))
            # navigate_case.add_step(TestStep("等待供应商信息菜单可点击", "wait_for_element", self.elements["base_info_menu_supplier_info"], timeout=5))
            navigate_case.add_step(TestStep("2.点击供应商信息菜单", "click", self.elements["base_info_menu_supplier_info"], timeout=2, retries=3))
            return self.execute_case(navigate_case, test_id=test_id)


    def test_add_supplier_basic_info(self, input_data=None, test_id=None):
        add_supplier_case = TestCase("添加供应商基本信息", browser="chrome", headless=False, keep_browser_open=True)
        add_supplier_case.add_step(TestStep("a.点击新增按钮", "click", self.elements["base_info_menu_supplier_info_add"], timeout=1, retries=3))
        
        # 编号
        add_supplier_case.add_step(TestStep("b.等待供应商编码输入框", "wait_for_element", self.elements["supplier_basicInfo_code"], timeout=5))
        supplier_basicInfo_code_text = get_input_value(input_data, "supplier_basicInfo_code", "")
        add_supplier_case.add_step(TestStep("1.输入供应商编码", "input_text", self.elements["supplier_basicInfo_code"], text=supplier_basicInfo_code_text, timeout=5))
        # 供应商简写
        supplier_basicInfo_short_name_text = get_input_value(input_data, "supplier_basicInfo_short_name", "")
        add_supplier_case.add_step(TestStep("2.输入供应商简称", "input_text", self.elements["supplier_basicInfo_short_name"], text=supplier_basicInfo_short_name_text, timeout=5))
        # 供应商全称
        supplier_basicInfo_full_name_text = get_input_value(input_data, "supplier_basicInfo_full_name", "")
        add_supplier_case.add_step(TestStep("3.输入供应商全称", "input_text", self.elements["supplier_basicInfo_full_name"], text=supplier_basicInfo_full_name_text, timeout=5))
        # 负责人
        supplier_basicInfo_owner_text = get_input_value(input_data, "supplier_basicInfo_owner", "")
        add_supplier_case.add_step(TestStep("4.输入负责人", "input_text", self.elements["supplier_basicInfo_owner"], text=supplier_basicInfo_owner_text, timeout=5))
        # 联系电话
        supplier_basicInfo_phone_text = get_input_value(input_data, "supplier_basicInfo_phone", "")
        add_supplier_case.add_step(TestStep("5.输入联系电话", "input_text", self.elements["supplier_basicInfo_phone"], text=supplier_basicInfo_phone_text, timeout=5))
        # 地址
        supplier_basicInfo_address_text = get_input_value(input_data, "supplier_basicInfo_address", "")
        add_supplier_case.add_step(TestStep("6.输入联系地址", "input_text", self.elements["supplier_basicInfo_address"], text=supplier_basicInfo_address_text, timeout=5))
        # 电子邮箱
        supplier_basicInfo_email_text = get_input_value(input_data, "supplier_basicInfo_email", "")
        add_supplier_case.add_step(TestStep("7.输入电子邮箱", "input_text", self.elements["supplier_basicInfo_email"], text=supplier_basicInfo_email_text, timeout=5))
        # # 国内外客户
        # supplier_basicInfo_domesticOrCrossboard_text = get_input_value(input_data, "supplier_basicInfo_domesticOrCrossboard", "")
        # add_supplier_case.add_step(TestStep("8.选择国家", "click", self.elements["supplier_basicInfo_domesticOrCrossboard_text"], timeout=2, retries=3))
        
        # #所属国家
        # supplier_basicInfo_country_text = get_input_value(input_data, "supplier_basicInfo_country", "")
        # add_supplier_case.add_step(TestStep("9.选择所属国家", "click", self.elements["supplier_basicInfo_country_text"], timeout=2, retries=3))
        # 业务担当
        supplier_basicInfo_business_owner_text = get_input_value(input_data, "supplier_basicInfo_business_owner", "")
        add_supplier_case.add_step(TestStep("10.输入业务担当", "input_text", self.elements["supplier_basicInfo_business_owner"], text=supplier_basicInfo_business_owner_text, timeout=2, retries=3))

        # 统一社会信用代码
        supplier_basicInfo_credit_code_text = get_input_value(input_data, "supplier_basicInfo_credit_code", "")
        add_supplier_case.add_step(TestStep("11.输入供应商信用代码", "input_text", self.elements["supplier_basicInfo_credit_code"], text=supplier_basicInfo_credit_code_text, timeout=5))
        # 开户银行
        supplier_basicInfo_bank_text = get_input_value(input_data, "supplier_basicInfo_bank", "")
        add_supplier_case.add_step(TestStep("12.输入开户银行", "input_text", self.elements["supplier_basicInfo_bank"], text=supplier_basicInfo_bank_text, timeout=5))

        # 银行账户
        supplier_basicInfo_bank_account_text = get_input_value(input_data, "supplier_basicInfo_bank_account", "")
        add_supplier_case.add_step(TestStep("13.输入银行账户", "input_text", self.elements["supplier_basicInfo_bank_account"], text=supplier_basicInfo_bank_account_text, timeout=5))
        # 备注
        supplier_basicInfo_remark_text = get_input_value(input_data, "supplier_basicInfo_remark", "ABC")
        add_supplier_case.add_step(TestStep("14.输入备注", "input_text", self.elements["supplier_basicInfo_remark"], text=supplier_basicInfo_remark_text, timeout=5))

        # 提交表单
        add_supplier_case.add_step(TestStep("15.提交表单", "click", self.elements["supplier_basicInfo_submit_btn"], timeout=5))
        return self.execute_case(add_supplier_case, test_id=test_id)
    
 
    def test_navigate_product_basic_info(self, input_data=None, test_id=None):
            navigate_case = TestCase("导航产品基本信息", browser="chrome", headless=False, keep_browser_open=True)
            navigate_case.add_step(TestStep("1.点击基础信息菜单", "click", self.elements["base_info_menu"], timeout=2, retries=3))
            # navigate_case.add_step(TestStep("等待产品信息菜单可点击", "wait_for_element", self.elements["base_info_menu_product_info"], timeout=5))
            navigate_case.add_step(TestStep("2.点击产品信息菜单", "click", self.elements["base_info_menu_product_info"], timeout=2, retries=3))
            return self.execute_case(navigate_case, test_id=test_id)


    def test_add_product_basic_info(self, input_data=None, test_id=None):
        add_product_case = TestCase("添加产品基本信息", browser="chrome", headless=False, keep_browser_open=True)
        add_product_case.add_step(TestStep("a.点击新增按钮", "click", self.elements["base_info_menu_product_info_add"], timeout=1, retries=3))
        # 图号
        product_basicInfo_code_text = get_input_value(input_data, "product_basicInfo_code", "")
        add_product_case.add_step(TestStep("b.等待产品编码输入框", "wait_for_element", self.elements["product_basicInfo_code"], timeout=5))
        add_product_case.add_step(TestStep("1.输入产品编码", "input_text", self.elements["product_basicInfo_code"], text=product_basicInfo_code_text, timeout=5))
        # 版本号
        product_basicInfo_version_text = get_input_value(input_data, "product_basicInfo_version", "")
        add_product_case.add_step(TestStep("2.输入产品版本号", "input_text", self.elements["product_basicInfo_version"], text=product_basicInfo_version_text, timeout=5))
        # 内部版本
        # product_basicInfo_internal_version_text = get_input_value(input_data, "product_basicInfo_internal_version", "")
        # add_product_case.add_step(TestStep("3.输入产品内部版本", "input_text", self.elements["product_basicInfo_internal_version"], text=product_basicInfo_internal_version_text, timeout=5))
        # # 品名
        product_basicInfo_full_name_text = get_input_value(input_data, "product_basicInfo_full_name", "")
        add_product_case.add_step(TestStep("4.输入产品全称", "input_text", self.elements["product_basicInfo_full_name"], text=product_basicInfo_full_name_text, timeout=5))
        # # 产品状态
        # product_basicInfo_status_text = get_input_value(input_data, "product_basicInfo_status", "")
        # add_product_case.add_step(TestStep("5.选择产品状态", "click", self.elements["product_basicInfo_status_select"], timeout=2, retries=3))
        # 颜色
        # product_basicInfo_color_text = get_input_value(input_data, "product_basicInfo_color", "")
        # add_product_case.add_step(TestStep("6.选择颜色", "click", self.elements["product_basicInfo_color_select"], timeout=2, retries=3))
        # 材质
        # product_basicInfo_material_text = get_input_value(input_data, "product_basicInfo_material", "")
        # add_product_case.add_step(TestStep("7.选择材质", "click", self.elements["product_basicInfo_material_select"], timeout=2, retries=3))
        # # 规格
        # product_basicInfo_specification_text = get_input_value(input_data, "product_basicInfo_specification", "")
        # add_product_case.add_step(TestStep("8.输入规格", "input", self.elements["product_basicInfo_specification"], product_basicInfo_specification_text, timeout=2, retries=3))
        # # 型号
        # product_basicInfo_model_text = get_input_value(input_data, "product_basicInfo_model", "")
        # add_product_case.add_step(TestStep("9.输入型号", "input", self.elements["product_basicInfo_model"], product_basicInfo_model_text, timeout=2, retries=3))
        # 阻燃等级
        product_basicInfo_fireproof_level_text = get_input_value(input_data, "product_basicInfo_fireproof_level", "")
        add_product_case.add_step(TestStep("10.选择阻燃等级", "click", self.elements["product_basicInfo_fireproof_level"], timeout=2, retries=3))
        # # 图纸构成
        # product_basicInfo_drawing_composition_text = get_input_value(input_data, "product_basicInfo_drawing_composition", "")
        # add_product_case.add_step(TestStep("11.输入图纸构成", "input", self.elements["product_basicInfo_drawing_composition"], product_basicInfo_drawing_composition_text, timeout=2, retries=3))
        # 量产单位
        # product_basicInfo_production_unit_text = get_input_value(input_data, "product_basicInfo_production_unit", "")
        # add_product_case.add_step(TestStep("12.输入量产单位", "input", self.elements["product_basicInfo_production_unit"], product_basicInfo_production_unit_text, timeout=2, retries=3))
        # # # 量产价格
        # product_basicInfo_production_price_text = get_input_value(input_data, "product_basicInfo_production_price", "")
        # add_product_case.add_step(TestStep("13.输入量产价格", "input", self.elements["product_basicInfo_production_price"], product_basicInfo_production_price_text, timeout=2, retries=3))
        # # 图纸
        # product_basicInfo_drawing_file_text = get_input_value(input_data, "product_basicInfo_drawing_file", "")
        # add_product_case.add_step(TestStep("14.上传图纸", "upload", self.elements["product_basicInfo_drawing_file"], product_basicInfo_drawing_file_text, timeout=2, retries=3))
        # 产品备注
        product_basicInfo_description_text = get_input_value(input_data, "product_basicInfo_description", "")
        add_product_case.add_step(TestStep("15.输入产品备注", "input_text", self.elements["product_basicInfo_description"], text=product_basicInfo_description_text, timeout=2, retries=3))

        # 提交表单
        add_product_case.add_step(TestStep("15.提交表单", "click", self.elements["product_basicInfo_submit_btn"], timeout=5))
        return self.execute_case(add_product_case, test_id=test_id)


    def execute_case(self, case: TestCase, test_id=None):
            case_dict = case.to_dict()
            if test_id:
                case_dict["test_id"] = test_id
            
            # self.print_case_dict(case_dict, case_id=test_id)
            response = requests.post(self.api_url, json=case_dict)
            result = response.json()
            print("接口原始返回内容：", response.text)
            # self.print_step_results(result.get("results", []), case_id=test_id)
            # self.print_case_result_details(case, result)

            self.print_case_result_details_with_wcwidth(case, result)   
            return result
    
    def print_step_results(self, results, case_id=None):
        # 列宽设置
        type_width = 16
        status_width = 10
        msg_width = 60
        # 表头
        header = f"{'步骤类型'.ljust(type_width)}|{'状态'.ljust(status_width)}|{'信息'.ljust(msg_width)}"
        print(f"\n测试执行结果 (case_id: {case_id}):")
        print(header)
        print('-' * (type_width + status_width + msg_width + 4))
        for step in results:
            step_type = str(step.get("type", "")).ljust(type_width)
            status = step.get("status", "")
            # 颜色只控制status
            if status == "success":
                status_str = "\033[32m" + status.ljust(status_width) + "\033[0m"
            elif status == "failed":
                status_str = "\033[31m" + status.ljust(status_width) + "\033[0m"
            else:
                status_str = status.ljust(status_width)
            msg = str(step.get("message", "")).replace('\n', ' ').ljust(msg_width)
            print(f"{step_type}|{status_str}|{msg}")

    def print_case_dict(self, case_dict, case_id=None):
        key_width = 16
        val_width = 60
        exclude_keys = {"browser", "headless", "keep_browser_open", "test_id"}
        print(f"\n测试用例内容 (case_id: {case_id}):")
        print('-' * (key_width + val_width + 1))
        for key, val in case_dict.items():
            if key in exclude_keys:
                continue
            if isinstance(val, list) and key == "steps":
                # 不打印 steps 表头，只打印每一步
                step_type_width = 12
                desc_width = 18
                locator_width = 40
                step_header = f"{'类型'.ljust(step_type_width)}|{'描述'.ljust(desc_width)}|{'定位'.ljust(locator_width)}"
                print(step_header)
                print('-' * (step_type_width + desc_width + locator_width + 2))
                for step in val:
                    step_type = str(step.get("action", ""))[:step_type_width-3] + "..." if len(str(step.get("action", ""))) > step_type_width else str(step.get("action", "")).ljust(step_type_width)
                    desc = str(step.get("desc", ""))[:desc_width-3] + "..." if len(str(step.get("desc", ""))) > desc_width else str(step.get("desc", "")).ljust(desc_width)
                    locator = str(step.get("locator", ""))[:locator_width-3] + "..." if len(str(step.get("locator", ""))) > locator_width else str(step.get("locator", "")).ljust(locator_width)
                    print(f"{step_type}|{desc}|{locator}")
            else:
                print(f"{str(key).ljust(key_width)}|{str(val)[:val_width-3] + '...' if len(str(val)) > val_width else str(val).ljust(val_width)}")
        print('-' * (key_width + val_width + 1))


    def print_case_result_details(self, case: TestCase, result: dict):
        # 列宽设置
        seq_width = 15
        desc_width = 22
        type_width = 40
        status_width = 10
        locator_width = 70
        msg_width = 100

        def trunc(val, width):
            val_str = str(val)
            count = 0
            out = ''
            for c in val_str:
                count += 2 if ord(c) > 127 else 1
                if count > width - 3:
                    out += '...'
                    break
                out += c
            return out.ljust(width)

        steps = case.to_dict().get("steps", [])
        results = result.get("results", [])

        header = f"{'序号'.ljust(seq_width)}|{'描述'.ljust(desc_width)}|{'步骤类型'.ljust(type_width)}|{'状态'.ljust(status_width)}|{'定位'.ljust(locator_width)}|{'信息'.ljust(msg_width)}"
        print("\n测试结果详细情况：")
        print(header)
        print('-' * (seq_width + desc_width + type_width + status_width + locator_width + msg_width + 5))

        for i, (step, res) in enumerate(zip(steps, results), 1):
            seq = trunc(i, seq_width)
            desc = trunc(step.get("desc", ""), desc_width)
            step_type = trunc(step.get("action", ""), type_width)
            status = res.get("status", "")
            if status == "success":
                status_str = "\033[32m" + trunc(status, status_width) + "\033[0m"
            elif status == "failed":
                status_str = "\033[31m" + trunc(status, status_width) + "\033[0m"
            else:
                status_str = trunc(status, status_width)
            locator = trunc(step.get("locator", ""), locator_width)
            msg = trunc(res.get("message", ""), msg_width)
            print(f"{seq}|{desc}|{step_type}|{status_str}|{locator}|{msg}")
        print('-' * (seq_width + desc_width + type_width + status_width + locator_width + msg_width + 5))

    def print_case_result_details_with_wcwidth(self, case: TestCase, result: dict):
        seq_width = 6
        desc_width = 22
        type_width = 14
        status_width = 10
        locator_width = 38
        msg_width = 44

        def trunc(val, width):
            val_str = str(val)
            count = 0
            out = ''
            for c in val_str:
                count += wcswidth(c)
                if count > width - 3:
                    out += '...'
                    break
                out += c
            pad_len = width - wcswidth(out)
            return out + ' ' * pad_len

        steps = case.to_dict().get("steps", []) if case else []
        results = result.get("results", []) if result else []

        # 用 trunc 处理表头，保证和内容对齐
        header = f"{trunc('序号', seq_width)}|{trunc('描述', desc_width)}|{trunc('步骤类型', type_width)}|{trunc('状态', status_width)}|{trunc('定位', locator_width)}|{trunc('信息', msg_width)}"
        print("\n测试结果详细情况：")
        print(header)
        print('-' * (seq_width + desc_width + type_width + status_width + locator_width + msg_width + 5))

        for i, (step, res) in enumerate(zip(steps, results), 1):
            seq = trunc(i, seq_width)
            desc = trunc(step.get("desc", ""), desc_width)
            step_type = trunc(step.get("action", ""), type_width)
            status = res.get("status", "")
            if status == "success":
                status_str = "\033[32m" + trunc(status, status_width) + "\033[0m"
            elif status == "failed":
                status_str = "\033[31m" + trunc(status, status_width) + "\033[0m"
            else:
                status_str = trunc(status, status_width)
            locator = trunc(step.get("locator", ""), locator_width)
            msg = trunc(res.get("message", ""), msg_width)
            print(f"{seq}|{desc}|{step_type}|{status_str}|{locator}|{msg}")
        print('-' * (seq_width + desc_width + type_width + status_width + locator_width + msg_width + 5))